/**
 * Copyright 2013 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2013-6-17 ������9:42:17
 */
package com.absir.bean.core;

import java.lang.reflect.Constructor;

import com.absir.bean.basis.Basis;
import com.absir.bean.basis.BeanScope;
import com.absir.core.kernel.KernelLang.CauseRuntimeException;
import com.absir.core.kernel.KernelString;

/**
 * @author absir
 * 
 */
@SuppressWarnings("unchecked")
public class BeanDefineType extends BeanDefineAbstract {

	/** constructor */
	Constructor<?> constructor;

	/** paramNames */
	String[] paramNames;

	/**
	 * @param beanName
	 * @param beanType
	 * @return
	 */
	public static String getBeanName(String beanName, Class<?> beanType) {
		return KernelString.isEmpty(beanName) ? KernelString.capitalize(beanType.getSimpleName()) : beanName;
	}

	/**
	 * @param beanType
	 * @return
	 */
	public static <T> Constructor<T> getBeanConstructor(Class<T> beanType) {
		Constructor<T>[] constructors = (Constructor<T>[]) beanType.getConstructors();
		if (constructors.length < 0) {
			throw new RuntimeException("can not find constructor of beanType = " + beanType);
		}

		for (Constructor<T> constructor : constructors) {
			if (constructor.getParameterTypes().length == 0 || constructor.getAnnotation(Basis.class) != null) {
				return constructor;
			}
		}

		return constructors[0];
	}

	/**
	 * @param constructor
	 * @return
	 */
	public static String[] getBeanParamNames(Constructor<?> constructor) {
		if (constructor.getParameterTypes().length == 0) {
			return null;
		}

		return BeanDefineAbstract.getBeanParamNames(constructor.getParameterAnnotations());
	}

	/**
	 * @param beanType
	 */
	public BeanDefineType(Class<?> beanType) {
		this(null, beanType);
	}

	/**
	 * @param beanName
	 * @param beanType
	 */
	public BeanDefineType(String beanName, Class<?> beanType) {
		this.constructor = BeanDefineType.getBeanConstructor(beanType);
		this.beanName = getBeanName(beanName, beanType);
		this.paramNames = constructor.getParameterTypes().length == 0 ? null : BeanDefineType
				.getBeanParamNames(constructor);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.android.bean.value.IBeanDefine#getBeanType()
	 */
	@Override
	public Class<?> getBeanType() {
		// TODO Auto-generated method stub
		return constructor.getDeclaringClass();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.android.bean.value.IBeanDefine#getBeanName()
	 */
	@Override
	public String getBeanName() {
		// TODO Auto-generated method stub
		return beanName;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.android.bean.value.IBeanDefine#getBeanObject()
	 */
	@Override
	public Object getBeanObject() {
		// TODO Auto-generated method stub
		return getBeanObject(constructor, paramNames);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.android.bean.value.IBeanDefine#getBeanScope()
	 */
	@Override
	public BeanScope getBeanScope() {
		// TODO Auto-generated method stub
		return BeanScope.PROTOTYPE;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.absir.android.bean.value.IBeanDefine#getBeanComponent()
	 */
	@Override
	public Object getBeanComponent() {
		// TODO Auto-generated method stub
		return constructor;
	}

	/**
	 * @param beanType
	 * @return
	 */
	protected <T> T getInstanceBean(Class<T> beanType) {
		return getBeanObject(BeanDefineType.getBeanConstructor(beanType));
	}

	/**
	 * @param constructor
	 * @return
	 */
	protected <T> T getBeanObject(Constructor<T> constructor) {
		return getBeanObject(constructor, BeanDefineType.getBeanParamNames(constructor));
	}

	/**
	 * @param constructor
	 * @param paramNames
	 * @return
	 */
	protected <T> T getBeanObject(Constructor<T> constructor, String[] paramNames) {
		try {
			if (paramNames == null) {
				return constructor.newInstance();
			}

			int length = paramNames.length;
			Object[] parameters = new Object[length];
			Class<?>[] parameterTypes = constructor.getParameterTypes();
			for (int i = 0; i < length; i++) {
				Object parameter = getBeanFactory().getBeanObject(paramNames[i], parameterTypes[i], true);
				if (parameter == null) {
					throw new RuntimeException("can not inject " + constructor.getDeclaringClass() + ".constructor = "
							+ constructor + " parameters [" + i + "]");
				}

				parameters[i] = parameter;
			}

			return constructor.newInstance(parameters);

		} catch (Exception e) {
			throw new CauseRuntimeException(e);
		}
	}
}
